package com.qianniu.napi.admin.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.jpay.ext.kit.PaymentKit;
import com.jpay.weixin.api.WxPayApi;
import com.jpay.weixin.api.WxPayApiConfig;
import com.qianniu.napi.admin.entity.*;
import com.qianniu.napi.admin.entity.penum.IotypeEnum;
import com.qianniu.napi.admin.entity.penum.RefundEnum;
import com.qianniu.napi.admin.entity.penum.StatusEnum;
import com.qianniu.napi.admin.entity.penum.XftypeEnum;
import com.qianniu.napi.admin.service.*;
import com.qianniu.napi.common.config.Constants;
import com.qianniu.napi.common.util.*;
import org.jdom.JDOMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;

@Service
public class QnpayImpl implements IQnpay {

    private static final Logger logger = LoggerFactory.getLogger(QnpayImpl.class);

    @Autowired
    private IMerchantService merchantService;

    @Autowired
    private IAccountService accountService;
    @Autowired
    private IAccountFlowService accountFlowService;
    @Autowired
    private IMaccountService maccountService;
    @Autowired
    private IMaccountFlowService maccountFlowService;

    @Autowired
    private IGoodsService goodsService;

    @Autowired
    private IOrderService orderService;

    @Autowired
    private IOrderRefundService orderRefundService;

    @Autowired
    private IUserService userService;
    @Autowired
    private IRedisService redisService;
    @Autowired
    private IOrderGoodsService orderGoodsService;

    //订单退款
    public Map cancelOrder(Order order, String appid) {

        // 退款
        Map map = getRefundmap(order.getMid(), appid, order.getOrderNo(), order.getRefundNo(),
                order.getRealpay(), order.getRealpay(), Constants.REFUND_SOURCE_UNSETTLED_FUNDS);
        Boolean refund = true;
        if (map == null || !map.containsKey("result_code") || !map.get("result_code").equals("SUCCESS")) {
            map.put("code", 1);
            map.put("info", "退款失败");
            refund = false;
//            return map;
        }

        //******* 写入[用户退款流水] START *******
        // ===== 1、退现金推余额:  ====
        // 全部在线全额退款，用户账户表无变化
        // 部分使用会员余额支付，用户账户有变化
        Long uid = order.getUid();
        Long mid = order.getMid();
        String sn = order.getOrderNo();
        Boolean flag = false;

        AccountFlow oaf = accountFlowService.selectOne(new EntityWrapper<AccountFlow>()
                .eq("uid", order.getUid())
                .eq("sn", order.getOrderNo())
        );
        AccountFlow af = new AccountFlow();
        // 总价 300=(券200+余额100=券200+会员余额49+赠送余额50<营销金额>）+实付1
        if(refund) {
            af.setRealpay(oaf.getRealpay());//1
            af.setAmount(oaf.getAmount());//49
        }else{
            af.setAmount(oaf.getAmount().add(oaf.getRealpay()));//49+1
        }
        af.setDikou(oaf.getDikou());//200+50

        User user = redisService.getUser();
        af.setNickname(user != null ? user.getNickName() : null);
        af.setHeadimg(user != null ? user.getAvatarPic() : null);
        af.setPhone(user != null ? user.getPhone() : null);

        af.setTotal(af.getRealpay().add(af.getAmount()).add(af.getDikou()));

        af.setRemark("取消订单退款");
        af.setIsConfirm(-1);
        af.setState(1);
        af.setMpid(oaf.getMpid());
        af.setUid(oaf.getUid());
        af.setMid(oaf.getMid());
        af.setTradetime(new Date());
        af.setCreatetime(new Date());
        af.setIotype(IotypeEnum.input.getVal());
        af.setXftype(XftypeEnum.refund.getVal());
        af.setSn(order.getRefundNo());
        af.insert();

        Account naccount = new Account();

        BigDecimal ambc = oaf.getAmount();
        BigDecimal mkbc = oaf.getDikou().subtract(order.getDiscount());// 250-200

        while (af != null && !flag) {
            Account account = accountService.selectOne(new EntityWrapper<Account>().eq("uid", uid));
            if (account == null) {
                logger.error(">>>account at uid=" + uid + " not exist and sn=" + sn);
                break;
            }

            Long version = account.getVersion();
            BigDecimal amount = account.getAmount();
            BigDecimal total = account.getTotal();
            BigDecimal mamount = account.getMamount();

            BigDecimal ljxiaofei = account.getLjxiaofei();

            naccount.setTotal(total.add(ambc).add(mkbc));
            naccount.setAmount(amount.add(ambc));
            naccount.setMamount(mamount.add(mkbc));
            naccount.setLjxiaofei(ljxiaofei.subtract(order.getRealpay()));

            naccount.setModifytime(new Date());
            naccount.setModifier(uid);
            naccount.setVersion(version + 1);

            flag = accountService.update(naccount, new EntityWrapper<Account>().eq("uid", uid).eq("version", version));
            if (flag) {
                af.setIsConfirm(1);
                af.setRuzhang(1);
                af.setConfirmDate(new Date());
                af.setLasttotal(account.getTotal());
                af.setLastamount(account.getAmount());
                af.updateById();
            }
        }

        // 3、退积分:一经使用，一概不退
        // 4、退优惠券:一经使用，一概不退

        //******* 写入[商家退款流水] *******
        MaccountFlow omaf = maccountFlowService.selectOne(new EntityWrapper<MaccountFlow>().eq("sn", order.getOrderNo()));

        MaccountFlow maf = new MaccountFlow();
        maf.setAmount(omaf.getAmount());
        maf.setMamount(omaf.getMamount());

        maf.setState(1);
        maf.setIsConfirm(-1);
        maf.setCreatetime(new Date());
        maf.setTradetime(new Date());
        maf.setIotype(IotypeEnum.output.getVal());
        maf.setXftype(XftypeEnum.refund.getVal());
        maf.setSn(af.getSn());
        maf.setMid(mid);
        maf.setMpid(order.getMpid());
        maf.setUid(uid);
        if (refund) maf.insert();

        flag = false;
        if (maf != null && refund) {
            flag = false;
            Maccount maccount = new Maccount();//取消订单
            while (!flag) {
                Maccount ma = maccountService.selectOne(new EntityWrapper<Maccount>().eq("mid", mid));

                maf.setIsConfirm(1);
                maf.setLasttotal(ma.getTotal());
                maf.setConfirmDate(new Date());

                Long version = ma.getVersion();
                BigDecimal amount = ma.getAmount();
                BigDecimal total = ma.getTotal();
                BigDecimal sale = ma.getSaleAmount();
                BigDecimal market = ma.getMamount();

                maccount.setTotal(total.subtract(maf.getAmount()));      // 总金额
                maccount.setSaleAmount(sale.subtract(maf.getAmount()));      // 累计销售额度
                maccount.setAmount(amount.subtract(maf.getAmount()));      // 可用余额
                maccount.setAmount(market.subtract(maf.getMamount()));      // 营销总金额

                maccount.setModifytime(new Date());
                maccount.setModifier(uid);
                maccount.setVersion(version + 1);

                maf.setIsConfirm(1);
                maf.setLasttotal(ma.getTotal());
                maf.setConfirmDate(new Date());
                maf.setModifytime(new Date());

                flag = maccountService.update(maccount, new EntityWrapper<Maccount>().eq("id", ma.getId()).eq("version", version));
                if (flag) maf.insertOrUpdate();
            }
        }

        // 更新订单+订单商品状态：已取消
        updateOrderStatus(order, StatusEnum.cancel.getVal());
        OrderRefund or = new OrderRefund();
        or.setStatus(RefundEnum.success.getVal());
        orderRefundService.update(or, new EntityWrapper<OrderRefund>().eq("refund_no", order.getRefundNo()));// 秒退，订单取消成功

        return map;
    }

    //订单退款
    public Map cancelOrderNoaccout(Order order, String appid) {

        // 退款
        Map map = getRefundmap(order.getMid(), appid, order.getOrderNo(), order.getRefundNo(),
                order.getRealpay(), order.getRealpay(), Constants.REFUND_SOURCE_UNSETTLED_FUNDS);
        Boolean refund = true;
        if (map == null || !map.containsKey("result_code") || !map.get("result_code").equals("SUCCESS")) {
            map.put("code", 1);
            map.put("info", "退款失败");
            refund = false;
            return map;
        }

        //******* 写入[用户退款流水] START *******
        // ===== 1、退现金推余额:  ====
        // 全部在线全额退款，用户账户表无变化
        // 部分使用会员余额支付，用户账户有变化
        Long uid = order.getUid();
        Long mid = order.getMid();
        String sn = order.getOrderNo();
        Boolean flag = false;

        AccountFlow oaf = accountFlowService.selectOne(new EntityWrapper<AccountFlow>()
                .eq("uid", order.getUid())
                .eq("sn", order.getOrderNo())
        );
        AccountFlow af = new AccountFlow();
        // 总价 300=(券200+余额100=券200+会员余额49+赠送余额50<营销金额>）+实付1
        if(refund) {
            af.setRealpay(oaf.getRealpay());//1
            af.setAmount(oaf.getAmount());//49
        }else{
            af.setAmount(oaf.getAmount().add(oaf.getRealpay()));//49+1
        }
        af.setDikou(oaf.getDikou());//200+50

        User user = redisService.getUser();
        af.setNickname(user != null ? user.getNickName() : null);
        af.setHeadimg(user != null ? user.getAvatarPic() : null);
        af.setPhone(user != null ? user.getPhone() : null);

        af.setTotal(af.getRealpay().add(af.getAmount()).add(af.getDikou()));

        af.setRemark("取消订单退款");
        af.setIsConfirm(-1);
        af.setState(1);
        af.setMpid(oaf.getMpid());
        af.setUid(oaf.getUid());
        af.setMid(oaf.getMid());
        af.setTradetime(new Date());
        af.setCreatetime(new Date());
        af.setIotype(IotypeEnum.input.getVal());
        af.setXftype(XftypeEnum.refund.getVal());
        af.setSn(order.getRefundNo());
        af.insert();

        Account naccount = new Account();

        BigDecimal ambc = oaf.getAmount();
        BigDecimal mkbc = oaf.getDikou().subtract(order.getDiscount());// 250-200

        while (af != null && !flag) {
            Account account = accountService.selectOne(new EntityWrapper<Account>().eq("uid", uid));
            if (account == null) {
                logger.error(">>>account at uid=" + uid + " not exist and sn=" + sn);
                break;
            }

            Long version = account.getVersion();
            BigDecimal amount = account.getAmount();
            BigDecimal total = account.getTotal();
            BigDecimal mamount = account.getMamount();

            BigDecimal ljxiaofei = account.getLjxiaofei();

            naccount.setTotal(total.add(ambc).add(mkbc));
            naccount.setAmount(amount.add(ambc));
            naccount.setMamount(mamount.add(mkbc));
            naccount.setLjxiaofei(ljxiaofei.subtract(order.getRealpay()));

            naccount.setModifytime(new Date());
            naccount.setModifier(uid);
            naccount.setVersion(version + 1);

            flag = accountService.update(naccount, new EntityWrapper<Account>().eq("uid", uid).eq("version", version));
            if (flag) {
                af.setIsConfirm(1);
                af.setRuzhang(1);
                af.setConfirmDate(new Date());
                af.setLasttotal(account.getTotal());
                af.setLastamount(account.getAmount());
                af.updateById();
            }
        }

        // 3、退积分:一经使用，一概不退
        // 4、退优惠券:一经使用，一概不退

        //******* 写入[商家退款流水] *******
        MaccountFlow omaf = maccountFlowService.selectOne(new EntityWrapper<MaccountFlow>().eq("sn", order.getOrderNo()));

        MaccountFlow maf = new MaccountFlow();
        maf.setAmount(omaf.getAmount());
        maf.setMamount(omaf.getMamount());

        maf.setState(1);
        maf.setIsConfirm(-1);
        maf.setCreatetime(new Date());
        maf.setTradetime(new Date());
        maf.setIotype(IotypeEnum.output.getVal());
        maf.setXftype(XftypeEnum.refund.getVal());
        maf.setSn(af.getSn());
        maf.setMid(mid);
        maf.setMpid(order.getMpid());
        maf.setUid(uid);
        if (refund) maf.insert();

        flag = false;
        if (maf != null && refund) {
            flag = false;
            Maccount maccount = new Maccount();//订单退款
            while (!flag) {
                Maccount ma = maccountService.selectOne(new EntityWrapper<Maccount>().eq("mid", mid));

                maf.setIsConfirm(1);
                maf.setLasttotal(ma.getTotal());
                maf.setConfirmDate(new Date());

                Long version = ma.getVersion();
                BigDecimal amount = ma.getAmount();
                BigDecimal total = ma.getTotal();
                BigDecimal sale = ma.getSaleAmount();
                BigDecimal market = ma.getMamount();

                maccount.setTotal(total.subtract(maf.getAmount()));      // 总金额
                maccount.setSaleAmount(sale.subtract(maf.getAmount()));      // 累计销售额度
                maccount.setAmount(amount.subtract(maf.getAmount()));      // 可用余额
                maccount.setAmount(market.subtract(maf.getMamount()));      // 营销总金额

                maccount.setModifytime(new Date());
                maccount.setModifier(uid);
                maccount.setVersion(version + 1);

                maf.setIsConfirm(1);
                maf.setLasttotal(ma.getTotal());
                maf.setConfirmDate(new Date());
                maf.setModifytime(new Date());

                flag = maccountService.update(maccount, new EntityWrapper<Maccount>().eq("id", ma.getId()).eq("version", version));
                if (flag) maf.insertOrUpdate();
            }
        }

        // 更新订单+订单商品状态：已取消
        updateOrderStatus(order, StatusEnum.cancel.getVal());
        OrderRefund or = new OrderRefund();
        or.setStatus(RefundEnum.success.getVal());
        orderRefundService.update(or, new EntityWrapper<OrderRefund>().eq("refund_no", order.getRefundNo()));// 秒退，订单取消成功

        return map;
    }

    public Boolean updateOrderStatus(Order o, int status) {

        // 更新订单+订单商品状态：已取消
        OrderGoods og = new OrderGoods();
        og.setStatus(status);
        og.setModifytime(new Date());
        if (o.getTransactionId() != null) og.setTransactionId(o.getTransactionId());
        og.setModifier(o.getUid());
        if (o.getRefundNo() != null) og.setRefundNo(o.getRefundNo());
        logger.info("-----------------------updateOrderStatus 111 =", JSON.toJSONString(o));

        orderGoodsService.update(og, new EntityWrapper<OrderGoods>().eq("oid", o.getId()));
        if (status == 2) {
            // ====== 待完善 ======
            // ====== 根据各种类型商品进行状态值变更 ======

        }

        o.setStatus(status);//已取消
        if (o.getTransactionId() != null) o.setTransactionId(o.getTransactionId());
        o.setModifytime(new Date());
        o.setModifier(o.getUid());
        o.setState(1);
        return o.updateById();
    }

    /*
     * 企业付款到零钱
     */
    public Map payPakect(Long mpid, String appid, User user, BigDecimal money, String desc, String ip) {

        MerchantApp mapp = redisService.getMApp(mpid);
        MerchantApp paymapp = null;
        int total = money.multiply(new BigDecimal(100)).intValue();
        if (mapp.getAspaympid() != null) {
            paymapp = redisService.getMApp(mapp.getAspaympid());
        }else{
            paymapp = mapp;
        }

        String mchid = paymapp.getMchid();
        Map<String, String> params = new HashMap<String, String>();
        params.put("mch_appid", paymapp.getAppid());
        params.put("mchid", mchid);
        String nonceStr = String.valueOf(System.currentTimeMillis());
        params.put("nonce_str", nonceStr);
        String partnerTradeNo = String.valueOf(System.currentTimeMillis());
        params.put("partner_trade_no", partnerTradeNo);
        params.put("openid", user.getOpenid());
        params.put("check_name", "NO_CHECK");
        params.put("amount", String.valueOf(total));
        params.put("desc", desc);
        params.put("spbill_create_ip", ip);

        // API-KEY
        params.put("sign", PaymentKit.createSign(params, paymapp.getPaykey()));

        String certPath = Constants.CERT_ROOT_PATH + mapp.getMcode() + "/" + mapp.getMpcode() + "/apiclient_cert.p12";
        if (paymapp != null){
            certPath = Constants.CERT_ROOT_PATH + paymapp.getMcode() + "/" + paymapp.getMpcode() + "/apiclient_cert.p12";
        }
        logger.debug(">>>params=" + JSON.toJSONString(params));
        logger.debug(">>>certPath=" + certPath);
        logger.debug(">>>mchid=" + mchid);

        // 提现
        String transfers = WxPayApi.transfers(params, certPath, mchid);
        Map<String, String> map = PaymentKit.xmlToMap(transfers);
        logger.debug(">>>map=" + map);

        return map;
    }

    /*
     * 企业付款到零钱
     */
    public Map payBank(Long mid, String appid, String openId, BigDecimal money, String desc, String ip) {

        Map map = new HashMap<String, String>();
        MerchantApp merchant = merchantService.getCurrentMerchant(mid, appid);

        Map<String, String> params = new HashMap<String, String>();
        params.put("mch_appid", appid);
        params.put("mchid", merchant.getMchid());
        String nonceStr = String.valueOf(System.currentTimeMillis());
        params.put("nonce_str", nonceStr);
        String partnerTradeNo = String.valueOf(System.currentTimeMillis());
        params.put("partner_trade_no", partnerTradeNo);
        params.put("openid", openId);
        params.put("check_name", "NO_CHECK");
        params.put("amount", "100");
        params.put("desc", desc);

//        String ip = IpKit.getRealIp(getRequest());
//        if (StrKit.isBlank(ip)) {
//            ip = "127.0.0.1";
//        }
//        params.put("spbill_create_ip", ip);
//
//        params.put("sign", PaymentKit.createSign(params, partnerKey));
//        System.out.println("certPath>"+certPath);
//        // 提现
//        String transfers = WxPayApi.transfers(params, certPath, mch_id);
//
//        logger.info("提现结果:" + transfers);
//        System.out.println("提现结果:" + transfers);
//
//        Map<String, String> map = PaymentKit.xmlToMap(transfers);
//        String return_code = map.get("return_code");
//        String result_code = null;
//        if (("SUCCESS").equals(return_code)) {
//            result_code = map.get("result_code");
//            if (("SUCCESS").equals(result_code)) {
//                //提现成功
//            } else {
//                //提现失败
//            }
//        }
//        renderText(transfers);

        return map;
    }

    /**
     * 微信小程序支付（目前仅用于充值）
     */
    public Map minappPay(Long mid, String appid, Long uid, BigDecimal money, String desc, String sn, String ip, Order order) {

        int total = money.multiply(new BigDecimal(100)).intValue();
        Map<String, String> map = new HashMap<String, String>();
        User user = userService.selectById(uid);
        MerchantApp mapp = redisService.getMApp();
        String notify = Constants.ROOT_URL +"/recharge/notify";
        String apikey = mapp.getPaykey();

        Map<String, String> params = WxPayApiConfig.New()
                .setAppId(appid)
                .setMchId(mapp.getMchid())
                .setPaternerKey(apikey)
                .setAttach(desc)
                .setBody(desc)
                .setSpbillCreateIp(ip)
                .setTotalFee(String.valueOf(total))
                .setTradeType(WxPayApi.TradeType.JSAPI)
                .setNotifyUrl(notify)
                .setOutTradeNo(sn)
                .setOpenId(user.getOpenid())
                .build();

        String xmlResult = WxPayApi.pushOrder(false, params);

        logger.info(xmlResult);
        Map<String, String> result = PaymentKit.xmlToMap(xmlResult);

        String return_code = result.get("return_code");
        String result_code = result.get("result_code");
        String return_msg = result.get("return_msg");
        if (!PaymentKit.codeIsOK(return_code) || !PaymentKit.codeIsOK(result_code)) {
            return result;
        }

        // 以下字段在return_code 和result_code都为SUCCESS的时候有返回
        String prepay_id = result.get("prepay_id");
        if (order != null) {
            order.setPrepayid(prepay_id);
            order.insertOrUpdate();
        }

        //封装调起微信支付的参数https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5
        map.put("appId", appid);
        map.put("timeStamp", System.currentTimeMillis() / 1000 + "");
        map.put("nonceStr", System.currentTimeMillis() + "");
        map.put("package", "prepay_id=" + prepay_id);
        map.put("signType", "MD5");

        String sign = PaymentKit.createSign(map, apikey);
        map.put("paySign", sign);
        return map;
    }

    /*
     *  申请退款
     *
     * @param mid          商户订单号
     * @param appid        分店appid
     * @param orderNo      商户订单号
     * @param refundNo     商户退款单号
     * @param realpay      订单金额
     * @param refund       退款金额
     * @param refundType   退款资金来源（默认传 "REFUND_SOURCE_UNSETTLED_FUNDS"）
     */
    public Map<String, Object> getRefundmap(Long mid, String appid, String orderNo, String refundNo,
                                            BigDecimal realpay, BigDecimal refund, String refundType) {

        // ****** 生成支付相关字段 ******
        // 微信支付价格值单位为：分 1代表1分，所以传过来的值必须*100；
        int totalfee = realpay.multiply(new BigDecimal("100")).intValue();
        int refundfee = refund.multiply(new BigDecimal("100")).intValue();

        // 订单编号
        String title = "申请退费";//订单标题

        // 时间戳
        String nonce_str = StringUtil.getGUID().substring(0, 16);
        SortedMap<String, Object> params = new TreeMap<String, Object>();
//        MerchantApp merchant = merchantService.getCurrentMerchant(mid, appid);
        MerchantApp mapp = redisService.getMApp();

        // API-KEY
        String apikey = mapp.getPaykey();

        params.put("appid", mapp.getAppid());
        params.put("mch_id", mapp.getMchid());
        params.put("nonce_str", nonce_str);//  时间戳

        params.put("out_trade_no", orderNo);  // 交易编号
        params.put("out_refund_no", refundNo);// 退款编号
        params.put("op_user_id", mapp.getMchid());// 操作员
        params.put("total_fee", totalfee);//   总价格
        params.put("refund_fee", refundfee);// 退费价格
        params.put("refund_account", refundType);//非必填： openid

        //获取sign
        logger.debug(">>>params=" + JSON.toJSONString(params));
        String sign = PayCommonUtil.createSign("UTF-8", params, apikey);//最后这个是自己设置的32位密钥
        logger.debug(">>>sign=" + sign);

        params.put("sign", sign);

        //转成XML
        String requestXML = PayCommonUtil.getRequestXml(params);
        String certPath = Constants.CERT_ROOT_PATH + mapp.getMcode() + "/" + mapp.getMpcode() + "/apiclient_cert.p12";

        //得到含有prepay_id的XML
        String resXml = null;
        try {
            logger.debug("=========requestXML=" + requestXML.toString());
            logger.debug("=========certPath=" + certPath);
            logger.debug("=========mapp=" + mapp.toString());
            resXml = HttpUtil.refundPost(Constants.WX_URL_PAY_REFUND, requestXML, certPath, mapp.getMchid());
        } catch (Exception e) {
            logger.error("refundPost error", e);
        }

        logger.debug(">>>resXml from refundPost");
        logger.info(resXml);

        Map<String, Object> result = new HashMap<String, Object>();
        //解析XML存入Map
        Map map = null;
        try {
            map = XMLUtil.doXMLParse(resXml);
        } catch (JDOMException | IOException e) {
            logger.error(">>>doXMLParse error", e);
            result.put("code", 5);
            result.put("msg", "支付异常");
            return result;
        }
        if (map.get("result_code").equals("FAIL")) {
            result.put("code", 5);
            result.put("msg", map.get("err_code_des"));
            return result;
        }

        //未结算金额不足  使用余额退款
        if (map != null && map.get("result_code").equals("FAIL") &&
                Constants.NOT_ENOUGH_MONEY.equals(map.get("err_code"))
                && Constants.REFUND_SOURCE_UNSETTLED_FUNDS.equals(refundType)) {

            return getRefundmap(mid, appid, orderNo, refundNo, realpay, refund, Constants.REFUND_SOURCE_RECHARGE_FUNDS);
        }

        logger.debug(">>>getRefundmap=" + JSON.toJSONString(map));
        return map;
    }
}
